﻿using System;
using System.Diagnostics;
using NAudio.Midi;
using PT.NoteDetection.Envelope;
using PT.NoteDetection.Midi;
using PT.NoteDetection.Wave;

namespace PT.NoteDetection {
    public class DetectionController {
        CaptureStream buffer;

        public AudioCapture Capture { get; set; }
        public EnvelopeNoteDetector Detector { get; private set; }
        public TimeService TimeService { get; private set; }

        public int Tempo {
            get { return TimeService.Tempo; }
            set { TimeService.Tempo = value; }
        }

        public long? StartCaptureTimestamp { get; private set; }
        public bool IsRunning { get; private set; }

        public event EventHandler<NoteDetectedEventArgs> NoteDetected;
       
        public DetectionController() {
            TimeService = new TimeService();
        }

        public void Start() {
            InitializeComponenets();

            Capture.Start();
            StartCaptureTimestamp = Stopwatch.GetTimestamp();
            IsRunning = true;
        }

        public void InitializeComponenets() {
            if (buffer == null) {
                buffer = new CaptureStream(10000);
            }
            buffer.Reset();

            if (Capture == null) {
                Capture = new WaveInAudioCapture();
            }
            Capture.Output = buffer;
            Capture.NewData += OnNewData;

            if (Detector == null) {
                Detector = new EnvelopeNoteDetector(buffer, TimeService = TimeService);
            }
        }

        public void Stop() {
            if (!IsRunning) {
                return;
            }

            Capture.Stop();
            Capture.NewData -= OnNewData;

            StartCaptureTimestamp = null;
            IsRunning = false;
        }

        void OnNewData(object sender, EventArgs args) {
            
            while (buffer.HasData(0)) {
                var notes = Detector.Read();

                if (notes != null && notes.Count > 0) {
                    foreach (var note in notes) {
                        if (note.CommandCode == MidiCommandCode.NoteOn) {
                            OnNoteDetected(note);
                        }
                    }
                }
            }
        }

        void OnNoteDetected(NoteEvent noteData) {
            var subscribers = NoteDetected;

            if (subscribers != null) {
                subscribers(this, new NoteDetectedEventArgs(noteData));
            }
        }
    }
}
